【Nacos】@NacosValue的使用与原理

您所在的位置:网站首页 @value map 【Nacos】@NacosValue的使用与原理

【Nacos】@NacosValue的使用与原理

2023-08-20 14:04| 来源: 网络整理| 查看: 265

在这里插入图片描述

在SpringCloud工程中,可以使用@RefreshScope+@Value实现配置文件内容变更后的动态刷新。

在SpringBoot工程中,可以使用@NacosValue来实现配置文件内容变更后的动态刷新。

@NacosValue的使用

引入依赖:

com.alibaba.boot nacos-config-spring-boot-starter 0.2.12

配置文件增加配置:

nacos: config: server-addr: 127.0.0.1:8848 bootstrap: enable: true log: enable: true data-id: order-service type: yaml auto-refresh: true # 开启自动刷新

@NacosValue的例子:

package com.morris.order.controller; import com.alibaba.nacos.api.config.annotation.NacosValue; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("order") public class NacosValueController { @NacosValue(value = "${user.age}", autoRefreshed = true) private Integer age2; @GetMapping("age2") public Integer getAge2() { return age2; } }

注意在SpringCloud项目中不能使用@NacosValue注解,虽然这个注解存在,但是其源码的实现不存在。

@NacosValue源码分析 NacosConfigEnvironmentProcessor

NacosConfigEnvironmentProcessor实现了EnvironmentPostProcessor,会在SpringBoot项目启动时调用postProcessEnvironment()方法,去Nacos配置中心拉取配置。

需要在配置文件中开启预加载nacos.config.bootstrap.enable=true。

com.alibaba.boot.nacos.config.autoconfigure.NacosConfigEnvironmentProcessor#postProcessEnvironment

public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { application.addInitializers(new NacosConfigApplicationContextInitializer(this)); nacosConfigProperties = NacosConfigPropertiesUtils .buildNacosConfigProperties(environment); if (enable()) { // nacos.config.bootstrap.enable=true System.out.println( "[Nacos Config Boot] : The preload log configuration is enabled"); loadConfig(environment); NacosConfigLoader nacosConfigLoader = NacosConfigLoaderFactory.getSingleton(nacosConfigProperties, environment, builder); LogAutoFreshProcess.build(environment, nacosConfigProperties, nacosConfigLoader, builder).process(); } }

com.alibaba.boot.nacos.config.autoconfigure.NacosConfigEnvironmentProcessor#loadConfig

private void loadConfig(ConfigurableEnvironment environment) { NacosConfigLoader configLoader = new NacosConfigLoader(nacosConfigProperties, environment, builder); configLoader.loadConfig(); // set defer NacosPropertySource deferPropertySources.addAll(configLoader.getNacosPropertySources()); }

从配置中心拉取配置加入到Environment。 com.alibaba.boot.nacos.config.util.NacosConfigLoader#loadConfig

public void loadConfig() { MutablePropertySources mutablePropertySources = environment.getPropertySources(); List sources = reqGlobalNacosConfig(globalProperties, nacosConfigProperties.getType()); for (NacosConfigProperties.Config config : nacosConfigProperties.getExtConfig()) { List elements = reqSubNacosConfig(config, globalProperties, config.getType()); sources.addAll(elements); } if (nacosConfigProperties.isRemoteFirst()) { // 默认为false,默认本地的配置优先级高 for (ListIterator itr = sources.listIterator(sources.size()); itr.hasPrevious();) { mutablePropertySources.addAfter( StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, itr.previous()); } } else { for (NacosPropertySource propertySource : sources) { mutablePropertySources.addLast(propertySource); } } } NacosConfigApplicationContextInitializer

NacosConfigApplicationContextInitializer注册负责监听Nacos配置,实现自动刷新配置

com.alibaba.boot.nacos.config.autoconfigure.NacosConfigApplicationContextInitializer#initialize

public void initialize(ConfigurableApplicationContext context) { singleton.setApplicationContext(context); environment = context.getEnvironment(); nacosConfigProperties = NacosConfigPropertiesUtils .buildNacosConfigProperties(environment); final NacosConfigLoader configLoader = NacosConfigLoaderFactory.getSingleton( nacosConfigProperties, environment, builder); if (!enable()) { logger.info("[Nacos Config Boot] : The preload configuration is not enabled"); } else { // If it opens the log level loading directly will cache // DeferNacosPropertySource release if (processor.enable()) { // nacos.config.bootstrap.enable=true processor.publishDeferService(context); // 添加监听器 configLoader .addListenerIfAutoRefreshed(processor.getDeferPropertySources()); } else { configLoader.loadConfig(); configLoader.addListenerIfAutoRefreshed(); } } final ConfigurableListableBeanFactory factory = context.getBeanFactory(); if (!factory .containsSingleton(NacosBeanUtils.GLOBAL_NACOS_PROPERTIES_BEAN_NAME)) { factory.registerSingleton(NacosBeanUtils.GLOBAL_NACOS_PROPERTIES_BEAN_NAME, configLoader.getGlobalProperties()); } }

遍历所有的Nacos配置文件,每个配置文件都添加一个监听器。 com.alibaba.boot.nacos.config.util.NacosConfigLoader#addListenerIfAutoRefreshed(java.util.List)

public void addListenerIfAutoRefreshed( final List deferNacosPropertySources) { for (DeferNacosPropertySource deferNacosPropertySource : deferNacosPropertySources) { NacosPropertySourcePostProcessor.addListenerIfAutoRefreshed( deferNacosPropertySource.getNacosPropertySource(), deferNacosPropertySource.getProperties(), deferNacosPropertySource.getEnvironment()); } }

要实现配置的自动刷新,需要在配置文件中开启nacos.config.auto-refresh=true。 com.alibaba.nacos.spring.core.env.NacosPropertySourcePostProcessor#addListenerIfAutoRefreshed

public static void addListenerIfAutoRefreshed( final NacosPropertySource nacosPropertySource, final Properties properties, final ConfigurableEnvironment environment) { // nacos.config.auto-refresh=true if (!nacosPropertySource.isAutoRefreshed()) { // Disable Auto-Refreshed return; } final String dataId = nacosPropertySource.getDataId(); final String groupId = nacosPropertySource.getGroupId(); final String type = nacosPropertySource.getType(); final NacosServiceFactory nacosServiceFactory = getNacosServiceFactoryBean( beanFactory); try { ConfigService configService = nacosServiceFactory .createConfigService(properties); Listener listener = new AbstractListener() { @Override public void receiveConfigInfo(String config) { String name = nacosPropertySource.getName(); NacosPropertySource newNacosPropertySource = new NacosPropertySource( dataId, groupId, name, config, type); newNacosPropertySource.copy(nacosPropertySource); MutablePropertySources propertySources = environment .getPropertySources(); // replace NacosPropertySource // 监听事件的处理,直接替换Environment中的配置 propertySources.replace(name, newNacosPropertySource); } }; if (configService instanceof EventPublishingConfigService) { // 通过EventPublishingConfigService进行代理 ((EventPublishingConfigService) configService).addListener(dataId, groupId, type, listener); } else { configService.addListener(dataId, groupId, listener); } } catch (NacosException e) { throw new RuntimeException( "ConfigService can't add Listener with properties : " + properties, e); } }

通过EventPublishingConfigService添加监听器,里面对监听器做了一层包装。 com.alibaba.nacos.spring.context.event.config.EventPublishingConfigService#addListener(java.lang.String, java.lang.String, java.lang.String, com.alibaba.nacos.api.config.listener.Listener)

public void addListener(String dataId, String group, String type, Listener listener) throws NacosException { Listener listenerAdapter = new DelegatingEventPublishingListener(configService, dataId, group, type, applicationEventPublisher, executor, listener); addListener(dataId, group, listenerAdapter); }

当配置中心的配置变更后,首先会回调DelegatingEventPublishingListener的receiveConfigInfo(),这里会调用被代理的监听器的receiveConfigInfo(),还会发布NacosConfigReceivedEvent事件。 com.alibaba.nacos.spring.context.event.config.DelegatingEventPublishingListener#receiveConfigInfo

public void receiveConfigInfo(String content) { // 调用被代理的监听器的receiveConfigInfo() onReceived(content); // 发布NacosConfigReceivedEvent事件 publishEvent(content); } private void publishEvent(String content) { NacosConfigReceivedEvent event = new NacosConfigReceivedEvent(configService, dataId, groupId, content, configType); applicationEventPublisher.publishEvent(event); } private void onReceived(String content) { delegate.receiveConfigInfo(content); } NacosValueAnnotationBeanPostProcessor postProcessPropertyValues()

NacosValueAnnotationBeanPostProcessor实现了InstantiationAwareBeanPostProcessorAdapter,其postProcessBeforeInitialization()方法会在Bean实例化之后和初始化之前执行,对Bean上加了@NacosValue注解的属性进行设置。

com.alibaba.spring.beans.factory.annotation.AbstractAnnotationBeanPostProcessor#postProcessPropertyValues

public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException { // 查找@NacosVaLue注解的属性和方法 InjectionMetadata metadata = this.findInjectionMetadata(beanName, bean.getClass(), pvs); try { // 为属性设置值 metadata.inject(bean, beanName, pvs); return pvs; } catch (BeanCreationException var7) { throw var7; } catch (Throwable var8) { throw new BeanCreationException(beanName, "Injection of @" + this.getAnnotationType().getSimpleName() + " dependencies is failed", var8); } } postProcessBeforeInitialization()

NacosValueAnnotationBeanPostProcessor实现了BeanPostProcessor,其postProcessBeforeInitialization()方法会在Bean实例化之后和初始化之前执行,主要负责收集带有@NacosValue注解且需要自动刷新的属性。

如果属性需要自动刷新,需要设置@NacosValue的autoRefreshed为true。

com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#postProcessBeforeInitialization

public Object postProcessBeforeInitialization(Object bean, final String beanName) throws BeansException { // 对Bean所有的属性进行处理 doWithFields(bean, beanName); // 对Bean所有的方法进行处理 doWithMethods(bean, beanName); return super.postProcessBeforeInitialization(bean, beanName); } private void doWithFields(final Object bean, final String beanName) { ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() { @Override public void doWith(Field field) throws IllegalArgumentException { NacosValue annotation = getAnnotation(field, NacosValue.class); doWithAnnotation(beanName, bean, annotation, field.getModifiers(), null, field); } }); } private void doWithAnnotation(String beanName, Object bean, NacosValue annotation, int modifiers, Method method, Field field) { if (annotation != null) { if (Modifier.isStatic(modifiers)) { return; } if (annotation.autoRefreshed()) { String placeholder = resolvePlaceholder(annotation.value()); if (placeholder == null) { return; } NacosValueTarget nacosValueTarget = new NacosValueTarget(bean, beanName, method, field, annotation.value()); // 将带有@NacosValue注解的属性加入到一个Map中 put2ListMap(placeholderNacosValueTargetMap, placeholder, nacosValueTarget); } } } 监听NacosConfigReceivedEvent

收到NacosConfigReceivedEvent事件时,通过反射调用上面收集好的属性和方法从而实现自动刷新。

com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor#onApplicationEvent

public void onApplicationEvent(NacosConfigReceivedEvent event) { // In to this event receiver, the environment has been updated the // latest configuration information, pull directly from the environment // fix issue #142 for (Map.Entry entry : placeholderNacosValueTargetMap .entrySet()) { String key = environment.resolvePlaceholders(entry.getKey()); String newValue = environment.getProperty(key); if (newValue == null) { continue; } List beanPropertyList = entry.getValue(); for (NacosValueTarget target : beanPropertyList) { String md5String = MD5Utils.md5Hex(newValue, "UTF-8"); boolean isUpdate = !target.lastMD5.equals(md5String); if (isUpdate) { target.updateLastMD5(md5String); Object evaluatedValue = resolveNotifyValue(target.nacosValueExpr, key, newValue); if (targethod == null) { setField(target, evaluatedValue); } else { setMethod(target, evaluatedValue); } } } } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3